Creating a package
@tinycld/create-package is the interactive scaffolder for new packages. One command produces a sibling 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 linked package — no biome.json ships in the new repo.) It’s the fastest way to go from idea to “linked into core and typechecking.”
One-shot
npx @tinycld/create-package my-feature
The positional argument is the slug - kebab-case, 3–40 chars. It becomes the npm package name (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:
- Attach mode — if your current directory has a
tinycld/child (i.e. an existing app shell checkout), the new package lands at./<slug>/next to it. This is the typical setup for active TinyCld contributors. - Bootstrap mode — if your current directory has no
tinycld/child, the scaffolder creates a wrapper directory./tinycld-<slug>/containing both a freshtinycld/app shell clone (when--linkis set or accepted) and the package itself at./tinycld-<slug>/<slug>/. You end up with a self-contained workspace you cancdinto and run.
The detection looks for a tinycld/ directory whose package.json declares "name": "tinycld" — 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:
- Human-readable name - defaults to title-cased slug, used in
manifest.nameand the nav label. - Description - one sentence, reused in the manifest,
package.json, and README. - Preset - pick
fullfor a data package (mail/contacts/drive shape) orsettings-onlyfor a settings-panel-only package (google-takeout-import shape). - Icon, nav order, shortcut - only for the full preset. The icon is any lucide-react-native name.
- Include a Go server? - full preset only. No if you only need JS hooks and migrations.
- Target directory - defaults to
./my-feature(a child of the current directory, which ends up sibling totinycld/). Must not exist or must be empty. - Link into the app shell now? - the scaffolder can clone the
tinycldapp shell (shallow) next to your new package and runpackages:linkfor you. Say yes to skip the manual steps below.
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/create-package my-feature \
--yes \
--preset full \
--icon check-square \
--no-server \
--no-link
See the create-package CLI reference for the full flag list.
Presets
full - data package
Matches @tinycld/contacts, @tinycld/mail, @tinycld/calendar, @tinycld/drive. You get:
manifest.tswithroutes,nav,collections,migrations,seed,sidebar, and optionallyserver.tinycld/<slug>/screens/{_layout, index, [id]}.tsx- list + detail routes.tinycld/<slug>/{collections.ts, types.ts}- pbtsdb registration and schema types.tinycld/<slug>/{sidebar.tsx, provider.tsx}- optional UI scaffolding.tinycld/<slug>/seed.ts- an async seed function with a working example write.pb-migrations/<timestamp>_create_<slug>.js- a starter PocketBase migration.server/{go.mod, register.go}- Go module stub if the Go prompt was yes.
settings-only - service package
Matches @tinycld/google-takeout-import. No routes, no nav entry, no collections, no server - just a settings panel:
manifest.tswith onlyname,slug,description,settings.tinycld/<slug>/settings/main.tsx- the panel component.tinycld/<slug>/types.ts- empty surface for any public type exports.
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 app shell now? prompt, the scaffolder has already cloned the tinycld app shell (shallow, into ../tinycld), run npm install, and linked your package in. 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 if you want to re-link later, do it yourself:
# from your workspace root, alongside the new package
git clone git@github.com:tinycld/tinycld.git
cd tinycld
npm install
npm run packages:link ../my-feature
npm run checks
The link command’s argument is a sibling-directory locator - a bare slug (my-feature) means ../my-feature, or you can pass a relative or absolute path. The package name itself comes from the sibling’s package.json, so there’s nothing to rewrite if your scope isn’t @tinycld.
npm run checks runs the app shell’s typecheck with your package linked. If it’s green, your scaffolded package is ready to develop in.
Then start the app:
npm run start # alias of `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 isn’t a separate npm package or git repo — it’s bundled inside the app shell at tinycld/packages/@tinycld/core/. Don’t try to install it as a dependency or clone it separately; resolution works because the sibling compiles inside the app shell’s tsconfig context once linked. See Screens for the full story.
For what each generated file means, walk through Anatomy. For linking the new package once it’s pushed, see Adding a package.